package network;

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.security.PublicKey;

public class Connection {

	private Connection(){}
	
	public static int MESSAGE_LENGTH = 10;
	//public Object lock=null;//to ensure non parallel access to the output stream or Msg list

	public Socket socket = null;//socket connection
	public PrintStream stdOut;//standard output
	//public InputStreamReader stdIn;//standard input

	public MessageReader msgIn;//read messages

	//set to information to be sent out
	//could cause problems if accessed before msg is sent...
	private volatile Message[] outMsg = new Message[MESSAGE_LENGTH];

	public NetworkingProtocol netProt=null;
	public byte connectionStatus= -1;//-1:no connection, 0: (dis)connecting, 1:connected
	public boolean flagDisconnect=false;
	public User user=null;
	public boolean localDisconnect = true;//always assume that we disconnect
	private boolean closed = false;
	public long lastAlive = 0;
	//the last time the connection was 'alive' (keep alive sent, or activity seen)
	public long lastSent = 0;
	//last time we sent something on this connection
	
	public void open(){
		close();//just make sure
		closed = false;
		outMsg = new Message[MESSAGE_LENGTH];
		flagDisconnect=false;
		localDisconnect = true;
	}
	
	public void close(){
		closed = true;
		try {
			msgIn.close();
			stdOut.close();
			socket.close();
		} catch (Exception e) {
		}finally{
			msgIn = null;
			stdOut = null;
			outMsg = null;
		}
	}

	public boolean equals(Connection c){
		if(c == null){
			return this == null;//cause if we're null, then it's the same...
		}
		return socket.equals(c.socket) && c.connectionStatus == connectionStatus;
	}
	/**
	 * 
	 * @return returns the number of messages that need to be sent.
	 */
	public int numSend(){
		if(closed){
			return 0;
		}
		int num = 0;
		if(outMsg != null){
			for(int i=0; i<outMsg.length; i++){
				if(outMsg[i] != null){
					num++;
				}
			}
		}
		return num;
	}
	
	public synchronized boolean addMsg(Message m){
		if(closed){
			return false;
		}
			boolean added = false;
			for(int i=0; i<outMsg.length; i++){
				if(outMsg[i] == null){
					outMsg[i] = m;
					added = true;
					break;
				}
			}
			if(!added){//couldn't add the msg. increase message queue size
				Message[] temp = outMsg.clone();
				outMsg = new Message[outMsg.length+10];
				System.arraycopy(temp, 0, outMsg, 0, temp.length);
				addMsg(m);//this WILL work.
			}
		return true;
	}

	/**
	 * drops the size of outMsg so that it will free resources.
	 * @return returns true if the list size was changed to be smaller.
	 */
	public synchronized boolean resetMsgList(){
		if(closed){
			return true;
		}
		boolean ret = true;

		int i = numSend();
		if(i == 0){
			outMsg = new Message[MESSAGE_LENGTH];
		}


		return ret;
	}

	/**
	 * This returns the list of messages, and clears it.
	 * @return
	 */
	public synchronized Message[] getMsgList(){
		if(closed){
			return null;
		}
		Message[] temp = outMsg.clone();
		outMsg = new Message[MESSAGE_LENGTH];
		return temp;
	}

	public void aliveNow(){
		lastAlive = System.currentTimeMillis();
	}
	
	public static Connection buildConnection(Socket s, PublicKey k, User u){
		Connection c = new Connection();
		c.socket = s;
		c.user = u;
		try {
			c.stdOut = new PrintStream( s.getOutputStream(), true);
			c.msgIn = new MessageReader( s.getInputStream(), k );
		} catch (IOException e) {
			// error, return null.
			try {
				s.close();
			} catch (IOException e1) {}
			
			return null;
		}
		return null;
	}
}
